|
| 1 | +# Blind Oracle: Remote Package Signing Service |
| 2 | + |
| 3 | +## Overview |
| 4 | +The Blind Oracle is a secure, remote signing service that holds GPG private keys and signs RPM packages and repository metadata on behalf of build servers. Build servers never have access to private keys, significantly reducing the attack surface. |
| 5 | + |
| 6 | +## Architecture |
| 7 | + |
| 8 | +### Components |
| 9 | + |
| 10 | +``` |
| 11 | +┌─────────────────────────────────────────────────────────────┐ |
| 12 | +│ Build Server (GitLab Runner) │ |
| 13 | +│ │ |
| 14 | +│ ┌──────────────┐ ┌─────────────────────────────────┐ │ |
| 15 | +│ │ gitlab-build │─────▶│ sign-package.sh │ │ |
| 16 | +│ │ -4.sh │ │ (Client Mode) │ │ |
| 17 | +│ └──────────────┘ └─────────────────────────────────┘ │ |
| 18 | +│ │ │ |
| 19 | +│ │ HTTPS POST │ |
| 20 | +│ │ {hash, dist, key_type} │ |
| 21 | +└─────────────────────────────────┼────────────────────────────┘ |
| 22 | + │ |
| 23 | + ▼ |
| 24 | +┌─────────────────────────────────────────────────────────────┐ |
| 25 | +│ Blind Oracle Server │ |
| 26 | +│ │ |
| 27 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 28 | +│ │ HTTP API (Flask/FastAPI) │ │ |
| 29 | +│ │ - POST /sign/rpm │ │ |
| 30 | +│ │ - POST /sign/repodata │ │ |
| 31 | +│ │ - GET /health │ │ |
| 32 | +│ └─────────────────────────────────────────────────────┘ │ |
| 33 | +│ │ │ |
| 34 | +│ ▼ │ |
| 35 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 36 | +│ │ Signing Engine │ │ |
| 37 | +│ │ - Key selection (Legacy/Modern) │ │ |
| 38 | +│ │ - GPG signing operations │ │ |
| 39 | +│ │ - Audit logging │ │ |
| 40 | +│ └─────────────────────────────────────────────────────┘ │ |
| 41 | +│ │ │ |
| 42 | +│ ▼ │ |
| 43 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 44 | +│ │ GPG Keyring (Offline Storage) │ │ |
| 45 | +│ │ - Legacy Key (4520AFA9) │ │ |
| 46 | +│ │ - Modern Key (CB2C73F04F3BE076) │ │ |
| 47 | +│ │ - Passphrases in secure vault │ │ |
| 48 | +│ └─────────────────────────────────────────────────────┘ │ |
| 49 | +└─────────────────────────────────────────────────────────────┘ |
| 50 | +``` |
| 51 | + |
| 52 | +## Security Model |
| 53 | + |
| 54 | +### Threat Mitigation |
| 55 | +1. **Build Server Compromise**: Private keys never leave the Oracle |
| 56 | +2. **Network Interception**: HTTPS + API token authentication |
| 57 | +3. **Unauthorized Signing**: Token-based access control + audit logs |
| 58 | +4. **Key Compromise**: Master key offline, only signing subkeys on Oracle |
| 59 | + |
| 60 | +### Authentication |
| 61 | +- API tokens per build server |
| 62 | +- Token rotation capability |
| 63 | +- Rate limiting per token |
| 64 | + |
| 65 | +### Audit Trail |
| 66 | +- All signing requests logged with: |
| 67 | + - Timestamp |
| 68 | + - Requesting server (token ID) |
| 69 | + - Package hash |
| 70 | + - Key used |
| 71 | + - Success/failure |
| 72 | + |
| 73 | +## API Specification |
| 74 | + |
| 75 | +### POST /sign/rpm |
| 76 | +Sign an RPM package. |
| 77 | + |
| 78 | +**Request:** |
| 79 | +```json |
| 80 | +{ |
| 81 | + "package_hash": "sha256:abc123...", |
| 82 | + "distribution": "el10-x86_64", |
| 83 | + "key_type": "modern", |
| 84 | + "token": "secret-api-token" |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +**Response:** |
| 89 | +```json |
| 90 | +{ |
| 91 | + "signature": "-----BEGIN PGP SIGNATURE-----...", |
| 92 | + "key_id": "CB2C73F04F3BE076", |
| 93 | + "timestamp": "2026-01-06T11:25:00Z" |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +### POST /sign/repodata |
| 98 | +Sign repository metadata (repomd.xml). |
| 99 | + |
| 100 | +**Request:** |
| 101 | +```json |
| 102 | +{ |
| 103 | + "repodata_hash": "sha256:def456...", |
| 104 | + "key_type": "modern", |
| 105 | + "token": "secret-api-token" |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +**Response:** |
| 110 | +```json |
| 111 | +{ |
| 112 | + "signature": "-----BEGIN PGP SIGNATURE-----...", |
| 113 | + "key_id": "CB2C73F04F3BE076", |
| 114 | + "timestamp": "2026-01-06T11:25:00Z" |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +## Implementation Phases |
| 119 | + |
| 120 | +### Phase 1: Local Prototype (Current) |
| 121 | +- ✅ `sign-package.sh` uses local GPG keys |
| 122 | +- ✅ Dual key selection logic |
| 123 | +- ✅ Deployed to GitLab runners |
| 124 | + |
| 125 | +### Phase 2: Oracle Server (Next) |
| 126 | +- [ ] Create Flask/FastAPI service |
| 127 | +- [ ] Implement signing endpoints |
| 128 | +- [ ] Add authentication/authorization |
| 129 | +- [ ] Deploy to secure server |
| 130 | +- [ ] Audit logging |
| 131 | + |
| 132 | +### Phase 3: Client Integration |
| 133 | +- [ ] Update `sign-package.sh` to detect Oracle mode |
| 134 | +- [ ] Implement HTTP client for signing requests |
| 135 | +- [ ] Fallback to local signing if Oracle unavailable |
| 136 | +- [ ] Token management |
| 137 | + |
| 138 | +### Phase 4: Production Hardening |
| 139 | +- [ ] HTTPS with mutual TLS |
| 140 | +- [ ] Hardware Security Module (HSM) integration |
| 141 | +- [ ] High availability / redundancy |
| 142 | +- [ ] Monitoring and alerting |
| 143 | +- [ ] Key rotation automation |
| 144 | + |
| 145 | +## Deployment Model |
| 146 | + |
| 147 | +### Development/Testing |
| 148 | +- Oracle runs on `winona7` (local development) |
| 149 | +- Build servers use API token for testing |
| 150 | + |
| 151 | +### Production |
| 152 | +- Oracle runs on dedicated, hardened server |
| 153 | +- Firewall rules: Only GitLab runners can access |
| 154 | +- Private keys stored in encrypted volume |
| 155 | +- Regular backups of audit logs |
| 156 | + |
| 157 | +## File Structure |
| 158 | +``` |
| 159 | +blind-oracle/ |
| 160 | +├── ARCHITECTURE.md # This file |
| 161 | +├── SIGNING_STRATEGY.md # GPG key strategy (existing) |
| 162 | +├── server/ |
| 163 | +│ ├── oracle-service.py # Main Flask/FastAPI app |
| 164 | +│ ├── signing_engine.py # GPG signing logic |
| 165 | +│ ├── auth.py # Token authentication |
| 166 | +│ ├── audit.py # Audit logging |
| 167 | +│ └── requirements.txt # Python dependencies |
| 168 | +├── client/ |
| 169 | +│ ├── sign-package.sh # Updated with Oracle support |
| 170 | +│ └── oracle-client.py # Python client library |
| 171 | +├── deployment/ |
| 172 | +│ ├── systemd/ |
| 173 | +│ │ └── oracle.service # Systemd unit file |
| 174 | +│ ├── nginx/ |
| 175 | +│ │ └── oracle.conf # Nginx reverse proxy config |
| 176 | +│ └── docker/ |
| 177 | +│ └── Dockerfile # Container image |
| 178 | +└── tests/ |
| 179 | + ├── test_signing.py # Unit tests |
| 180 | + └── test_integration.py # Integration tests |
| 181 | +``` |
| 182 | + |
| 183 | +## Next Steps |
| 184 | +1. Implement basic Flask service with `/sign/rpm` endpoint |
| 185 | +2. Create Python signing engine using `gpg` library |
| 186 | +3. Update `sign-package.sh` to support Oracle mode |
| 187 | +4. Test end-to-end signing workflow |
| 188 | +5. Add authentication and audit logging |
0 commit comments